/*
 * main_window.cpp
 *
 *  Created on: 12.06.2013
 *      Author: SpirTanol
 */

#include "ui/main_window.h"
#include "ui/tab_search.h"
#include "ui/tab_watch.h"
#include "ui/tab_setup.h"
#include "ui/tab_char_curve.h"
#include "ui/tab_crash_curve.h"
#include "device/data_thread.h"
#include "events/data_event.h"
#include "packets/protocol_packet.h"
#include "packets/crash_protocol_packet.h"
#include "packets/watch_data_packet.h"
#include "packets/crash_oscillogram_packet.h"
#include "device/device_info.h"
#include "events/event_protocol_event.h"
#include "events/crash_protocol_event.h"
#include "events/app_protocol_event.h"
#include "report_creator.h"
#include "app_config.h"
#include "ui/config_dialog.h"

// QT
#include <QTabWidget>
#include <QToolBar>
#include <QStatusBar>
#include <QApplication>
#include <QLabel>
#include <QDebug>
#include <QLineEdit>
#include <QIntValidator>
#include <QBoxLayout>
#include <QSpacerItem>
#include <QToolButton>
#include <QPixmap>
#include <QFileDialog>
#include <QFileInfo>

// OS
#include <windows.h>
//======================================================================================
int gDeviceVersion = 0;
//======================================================================================
MainWindow::MainWindow() : QMainWindow(), oThread(0)
{
	setWindowIcon(QPixmap(":/img/icon.ico"));

	AppConfig::i()->load();

	createUI();
	createToolBar();

	setStatusBar(new QStatusBar());

	oLabelConnection = new QLabel(trUtf8("-"));
	oLabelConnection->setAlignment(Qt::AlignCenter);
	statusBar()->addPermanentWidget(oLabelConnection);

	updateTitle();

	loadLastConnection();
}

MainWindow::~MainWindow()
{
	saveLastConnection();
	disconnectFromDevice();
	AppConfig::i()->save();
}
//======================================================================================
void MainWindow::createUI()
{
	oTabWidget = new QTabWidget();
	setCentralWidget(oTabWidget);

	oTabSearch = new TabSearch(this);
	oTabWidget->addTab(oTabSearch, trUtf8("Подключение"));

	oTabWatch = new TabWatch(this, &oProtocol);
	oTabWidget->addTab(oTabWatch, trUtf8("Наблюдение и управление"));

	oTabSetup = new TabSetup(this);
	oTabWidget->addTab(oTabSetup, trUtf8("Настройка"));

	oTabCharCurve = new TabCharCurve(this);
	oTabWidget->addTab(oTabCharCurve, trUtf8("Токо-временная характеристика"));

	oTabCrashCurve = new TabCrashCurve(this);
	oTabWidget->addTab(oTabCrashCurve, trUtf8("Аварийная осциллограмма"));

	oTabWidget->setTabEnabled(1, false);
	oTabWidget->setTabEnabled(2, false);
	oTabWidget->setTabEnabled(3, false);
	oTabWidget->setTabEnabled(4, false);
}

void MainWindow::createToolBar()
{
	QToolBar *bar = new QToolBar();
	bar->setOrientation(Qt::Horizontal);
	addToolBar(Qt::TopToolBarArea, bar);

	oBtnReport = new QToolButton();
	oBtnReport->setIcon(QPixmap(":/img/report.png"));
	oBtnReport->setToolTip(trUtf8("Сгенерировать отчёт"));
	oBtnReport->setEnabled(false);
	bar->addWidget(oBtnReport);
	connect(oBtnReport, SIGNAL(clicked()), this, SLOT(onBtnCreateReport()));

	QToolButton *btn = new QToolButton();
	btn->setIcon(QPixmap(":/img/service_manager.png"));
	btn->setToolTip(trUtf8("Настройки программы"));
	bar->addWidget(btn);
	connect(btn, SIGNAL(clicked()), this, SLOT(onBtnShowAppConfig()));

	btn = new QToolButton();
	btn->setIcon(QPixmap(":/img/help.png"));
	btn->setToolTip(trUtf8("Помощь"));
	bar->addWidget(btn);
	connect(btn, SIGNAL(clicked()), this, SLOT(onBtnShowHelp()));

	QWidget *w = new QWidget();
	bar->addWidget(w);

	QBoxLayout *l = new QBoxLayout(QBoxLayout::LeftToRight);
	w->setLayout(l);

	l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred));

	l->addWidget(new QLabel(trUtf8("Код доступа: ")));

	oEditPassword = new QLineEdit();
	oEditPassword->setEchoMode(QLineEdit::Password);
	oEditPassword->setValidator(new QIntValidator(0, 9999, this));
	oEditPassword->setMaxLength(4);
	oEditPassword->setFixedWidth(50);
	oEditPassword->setEnabled(false);
	l->addWidget(oEditPassword);
	connect(oEditPassword, SIGNAL(editingFinished()), this, SLOT(onPasswordEditingFinished()));
}
//======================================================================================
void MainWindow::showMessage(const QString &msg, int time)
{
	statusBar()->showMessage(msg, time);
}
//======================================================================================
void MainWindow::connectToDevice(const DeviceInfo *dev)
{
	disconnectFromDevice();
	gDeviceVersion = dev->version();

	oThread = new DataThread(dev);
	connect(oThread, SIGNAL(dataEvent(const DataEvent*)), this, SLOT(onDataEvent(const DataEvent*)));
	oThread->openConnect();
}

void MainWindow::disconnectFromDevice()
{
	if (oThread)
	{
		oThread->closeConnect();
		delete oThread;
		oThread = 0;

		for (EventList::Iterator it = oProtocol.begin();
			it != oProtocol.end(); ++it)
		delete *it;
		oProtocol.clear();
		updateTitle();
	}
}
//======================================================================================
void MainWindow::onDataEvent(const DataEvent *e)
{
	extern const QString cgGreenColor;
	extern const QString cgRedColor;

	switch (e->type())
	{
	case DET_CONNECT:
		oTabWidget->setTabEnabled(1, true);
		oTabWidget->setTabEnabled(2, true);
		oTabWidget->setTabEnabled(3, true);

		oTabWidget->setCurrentIndex(1);

		oLabelConnection->setText(trUtf8("Подключение"));
		oLabelConnection->setStyleSheet(cgGreenColor);

		showMessage(trUtf8("Соединение установлено"), 10000);
		oTabSearch->onConnected();
		addEvent(new AppProtocolEvent(AEC_CONNECT));
		oEditPassword->setEnabled(true);
		oBtnReport->setEnabled(true);
		updateTitle();
		break;
	case DET_DISCONNECT:
		oTabWidget->setTabEnabled(1, false);
		oTabWidget->setTabEnabled(2, false);
		oTabWidget->setTabEnabled(3, false);
		oTabWidget->setTabEnabled(4, false);

		oTabWidget->setCurrentIndex(0);

		oLabelConnection->setText(trUtf8("-"));
		oLabelConnection->setStyleSheet(QString());

		showMessage(trUtf8("Соединение разорвано"), 10000);
		oTabSearch->onDisconnected();

		oEditPassword->clear();
		oEditPassword->setEnabled(false);
		oBtnReport->setEnabled(false);
		break;
	case DET_ERROR:
		switch (static_cast<const DataEventError*>(e)->code())
		{
		case DataEventError::OPEN_ERROR:
			showMessage(trUtf8("Ошибка соединения: невозможно открыть порт!"), 0);
			delete oThread;
			oThread = 0;
			break;
		case DataEventError::CONNECT_FAIL:
			showMessage(trUtf8("Не удалось подключиться к устройству"), 0);
			delete oThread;
			oThread = 0;
			break;
		case DataEventError::LOST_CONNECTION:
			showMessage(trUtf8("Соединение потеряно"), 0);
			oLabelConnection->setText(trUtf8("Нет связи"));
			oLabelConnection->setStyleSheet(cgRedColor);
			addEvent(new AppProtocolEvent(AEC_LOST_CONNECTION));
			break;
		case DataEventError::UNEXPECTED_MODIFIER:
			showMessage(trUtf8("Модификация устройства не соответствует ожидаемой"), 0);
			delete oThread;
			oThread = 0;
			break;
		default: break;
		}
		break;
	case DET_RECONNECTION:
		showMessage(trUtf8("Соединение восстановлено"), 10000);
		oLabelConnection->setText(trUtf8("Подключение"));
		oLabelConnection->setStyleSheet(cgGreenColor);
		addEvent(new AppProtocolEvent(AEC_RECONNECT));
		break;
	case DET_GET_PROTOCOLS:
		updateProtocol(static_cast<const DataEventGetProtocol*>(e));
		break;
	case DET_TIME_WRITED:
		showMessage(trUtf8("Время настроено"), 5000);
		break;
	case DET_TIME_FAIL:
		showMessage(trUtf8("Не удалось настроить время"), 5000);
		break;
	case DET_CONFIG:
	{
		const ConfigEvent *ce = static_cast<const ConfigEvent*>(e);
		switch (ce->eventType())
		{
		case CET_GET:
			showMessage(trUtf8("Настройки получены"), 5000);
			break;
		case CET_SETUPED:
			addEvent(new AppProtocolEvent(AEC_CONFIG_WRITED));
			showMessage(trUtf8("Настройки записаны"), 5000);
			break;
		case CET_SETUP_ERROR:
			showMessage(trUtf8("Ошибка записи настроек"), 0);
			break;
		}
	}
		break;
	case DET_RESET_CRASH_PROTOCOL:
		for (EventList::Iterator it = oProtocol.begin();
				it != oProtocol.end();)
		{
			if ((*it)->type() == PET_CRASH)
			{
				delete *it;
				it = oProtocol.erase(it);
				continue;
			}

			++it;
		}

		showMessage(trUtf8("Протокол аварий сброшен"), 5000);
		break;
	case DET_RESET_CRASH_PROTOCOL_FAIL:
		showMessage(trUtf8("Ошибка сброса протокола аварий"), 5000);
		break;
	case DET_RESET_WORK_PROTOCOL:
		for (EventList::Iterator it = oProtocol.begin();
				it != oProtocol.end();)
		{
			if ((*it)->type() == PET_DEVICE)
			{
				delete *it;
				it = oProtocol.erase(it);
				continue;
			}

			++it;
		}

		showMessage(trUtf8("Протокол работы двигателя сброшен"), 5000);
		break;
	case DET_RESET_WORK_PROTOCOL_FAIL:
		showMessage(trUtf8("Ошибка сброса протокола работы двигателя"), 5000);
		break;
	case DET_RESET_WORK_TIME:
		showMessage(trUtf8("Наработка двигателя сброшена"), 5000);
		break;
	case DET_RESET_WORK_TIME_FAIL:
		showMessage(trUtf8("Ошибка сброса наработки двигателя"), 5000);
		break;
	case DET_CRASH_OSCILLOGRAM_GET:
		oTabWidget->setTabEnabled(4, static_cast<const CrashEventOscillogramGet*>(e)->
				oscillogram()->code() != 0);
		break;
	default: break;
	}

	oTabWatch->onDataEvent(e);
	oTabSetup->onDataEvent(e);
	oTabCharCurve->onDataEvent(e);
	oTabCrashCurve->onDataEvent(e);

	delete e;
}
//======================================================================================
const DeviceInfo *MainWindow::deviceInfo() const
{
	return oThread->device();
}
//======================================================================================
void MainWindow::updateProtocol(const DataEventGetProtocol *e)
{
	const ProtocolPacket *events = static_cast<const ProtocolPacket*>(e->eventProtocol());
	const CrashProtocolPacket *crashes = static_cast<const CrashProtocolPacket*>(e->crashProtocol());

	// события
	for (int i = 0; i < 10; ++i)
	{
		bool found = false;

		for (EventList::Iterator it = oProtocol.begin();
			it != oProtocol.end() && !found; ++it)
		{
			if ((*it)->type() == PET_DEVICE)
			{
				EventProtocolEvent *ep = static_cast<EventProtocolEvent*>(*it);
				found = (ep->time().toTime_t() == events->time(i) &&
						ep->code() == events->code(i));
			}
		}

		if (found) continue;

		if (events->code(i) != 0)
			oProtocol.push_back(new EventProtocolEvent(events->event(i)));
	}
	// аварии
	for (int i = 0; i < 10; ++i)
	{
		CrashData dc = crashes->crash(i);

		bool found = false;

		for (EventList::Iterator it = oProtocol.begin();
			it != oProtocol.end() && !found; ++it)
		{
			if ((*it)->type() == PET_CRASH)
			{
				CrashProtocolEvent *ep = static_cast<CrashProtocolEvent*>(*it);
				found = (ep->time().toTime_t() == dc.oTime &&
						ep->code() == dc.oCode);
			}
		}

		if (found) continue;

		if (dc.oCode != 0)
			oProtocol.push_back(new CrashProtocolEvent(dc));
	}

	qSort(oProtocol.begin(), oProtocol.end(), ProtocolEvent::SortDesc());
	oTabWatch->updateProtocol();
}

void MainWindow::addEvent(ProtocolEvent *e)
{
	oProtocol.push_front(e);
	qSort(oProtocol.begin(), oProtocol.end(), ProtocolEvent::SortDesc());

	oTabWatch->updateProtocol();
}
//======================================================================================
void MainWindow::readConfig()
{
	oThread->readConfig();
	showMessage(trUtf8("Чтение настроек..."), 5000);
}

void MainWindow::writeConfig(ConfigPacket *packet)
{
	oThread->writeConfig(packet);
	showMessage(trUtf8("Запись настроек..."), 5000);
}

void MainWindow::writeTime(uint time)
{
	oThread->writeTime(time);
	showMessage(trUtf8("Настройка времени..."), 5000);
}
//======================================================================================
void MainWindow::resetCrashProtocol()
{
	oThread->resetCrahProtocol();
	showMessage(trUtf8("Очистка протокола аварий..."), 5000);
}

void MainWindow::resetWorkProtocol()
{
	oThread->resetWorkProtocol();
	showMessage(trUtf8("Очистка протокола работы двигателя..."), 5000);
}

void MainWindow::resetWorkTime()
{
	oThread->resetWorkTime();
	showMessage(trUtf8("Сброс наработки двигателя..."), 5000);
}

void MainWindow::changeAppendOut()
{
	oThread->changeAppendOut();
}

void MainWindow::commandEngine(int command)
{
	addEvent(new AppProtocolEventEngine(command));
	oThread->commandEngine(command);
}
//======================================================================================
void MainWindow::onPasswordEditingFinished()
{
	oThread->setPassword(oEditPassword->text().toInt());
}

bool MainWindow::hasPassword() const
{
	return !oEditPassword->text().isEmpty();
}
//======================================================================================
void MainWindow::focusEditPassword()
{
	oEditPassword->setFocus();
}
//======================================================================================
void MainWindow::runAutoConfig()
{
	oThread->runAutoConfig();
}
//======================================================================================
void MainWindow::onBtnCreateReport()
{
	QString filePath(QFileDialog::getSaveFileName(this, trUtf8("Сохранить отчёт"),
		AppConfig::i()->lastPath() + (trUtf8("/Отчёт ") +
				QDateTime::currentDateTime().toString("dd.MM.yyyy hh-mm") + ".pdf"), "PDF (*.pdf)"));
	if (filePath.isEmpty()) return;

	showMessage(trUtf8("Генерация отчёта..."), 3000);

	const CrashPlot *crashPlot = 0;
	if (oTabCrashCurve->crashPlotWriteTime() > 0)
		crashPlot = oTabCrashCurve->crashPlot();

	ReportCreator *rc = new ReportCreator();
	rc->create(filePath, oThread->device(), oThread->watchPacket(), oThread->configPacket(), &oProtocol, oTabWatch->watchPlot(),
			oTabWatch->tempPlot(), oTabCharCurve->runConfigPlot(), crashPlot, oTabCrashCurve->crashPlotWriteTime(), oTabCrashCurve->crashCode());
	delete rc;

	QFileInfo fi(filePath);
	AppConfig::i()->setLastPath(fi.dir().path());

	showMessage(trUtf8("Отчёт готов"), 6000);
}
//======================================================================================
void MainWindow::onBtnShowHelp()
{
	ShellExecuteA(0, "Open", (QApplication::applicationDirPath() + "/../help.chm").toLocal8Bit().data(), 0, 0, SW_SHOWNORMAL);
}
//======================================================================================
void MainWindow::onBtnShowAppConfig()
{
	ConfigDialog *configDialog = new ConfigDialog(AppConfig::i()->autoConnect(),
			AppConfig::i()->scaleType(), AppConfig::i()->interval());

	if (configDialog->exec() == QDialog::Accepted)
	{
		AppConfig::i()->setAutoConnect(configDialog->autoConnect());
		AppConfig::i()->setScaleType(configDialog->scaleType());
		AppConfig::i()->setInterval(configDialog->interval());

		oTabCharCurve->setScaleType(configDialog->scaleType());
		oTabWatch->setPlotInterval(configDialog->interval());
	}

	delete configDialog;
}
//======================================================================================
void MainWindow::saveLastConnection()
{
	FILE *f = fopen("../data/last_connection.dat", "wb");
	if (!f) return;
	if (oThread)
		fwrite(oThread->device(), sizeof(DeviceInfo), 1, f);

	fclose(f);
}

void MainWindow::loadLastConnection()
{
	if (!AppConfig::i()->autoConnect()) return;

	FILE *f = fopen("../data/last_connection.dat", "rb");
	if (!f) return;

	while (!feof(f))
	{
		void *memDev = malloc(sizeof(DeviceInfo));
		if (!fread(memDev, sizeof(DeviceInfo), 1, f))
		{
			free(memDev);
			break;
		}
		DeviceInfo *dev = newIn<DeviceInfo>(memDev);
		connectToDevice(dev);
	}

	fclose(f);
}
//======================================================================================
void MainWindow::updateTitle()
{
	if (oThread)
	{
		const DeviceInfo* dev = oThread->device();
		setWindowTitle(qApp->applicationName() + QObject::trUtf8(" [УЗД-11 ") +
				QString::number(dev->curMin()) + "-" + QString::number(dev->curMax()) + " v" +
				QString::number(dev->version() * 0.01, 'F', 2) + " / " + QString::number(dev->addr()) + " / COM" +
				QString::number(dev->port()) + "]");
		return;
	}
	setWindowTitle(qApp->applicationName());
}
